#ifndef _ENGINE_HPP_
#define _ENGINE_HPP_

#include "definitions.h"
#include "event_list.h"
#include "queue.hpp"
#include <iostream>
#include <vector>
using namespace std;

#define N_PRINTS 100
#define TRACEFILE "trace.dat"

class Engine {
public:
  Engine()
    : n_events(0), time(0)
#ifdef LOG
    , tracefile(*new ofstream(TRACEFILE))
#endif
  {}

  ~Engine() {
#ifdef LOG
    delete &tracefile;
#endif
  }

  void add_queues(vector<Queue *> &q) {
    queues.reserve(q.size());
    for(unsigned int i=0; i<q.size(); ++i) {
      q[i]->set_event_list(&event_list);
      queues.push_back(q[i]);
    }
  }

#ifdef LOG
  void print_state() {
    tracefile << *this << endl;
  }
#endif

  void step(Queue *&src, Queue *&dst, Job *&job, double &event_time) {
      Event_Notice *current_event = event_list.fetch();
      ++n_events;

      //update the clock
      old_time = time;
      time = current_event->get_occ_time();

      //event-action
      src = current_event->get_source();
      dst = current_event->get_dest();
      job = current_event->get_job();
      event_time = time;
      /*
#ifdef LOG
      cerr << "FIRING [" << time << "]"
	   << " departure from " << *src
	   << " to " << *dst << endl;
#endif
      */
      src->departure(job, time);
      dst->arrival(job, time);
      delete current_event; //clean-up

#ifdef LOG
      if(n_events <= N_PRINTS)
	print_state();
#endif
  }

  void stats(ostream &os = cout) {
    os << "* --- *\n"
       << "time: " << time << " (" << n_events << " events)" << endl;
    for(unsigned int i=0; i<queues.size(); ++i) {
      stats_t s;
      queues[i]->stats(&s, time);
      os << "\n+++ station " << *queues[i] << endl
	   << "+ throughput: " << s.throughput << endl
	   << "+ utilization: " << s.utilization << endl
	   << "+ load: " << s.load << endl
	   << "+ waiting time: " << s.waiting_time << endl
	   << "- n. jobs: " << s.n_jobs << endl
	   << "- n. departures: " << s.n_departures << endl
	   << "- busy time: " << s.busy_time << endl
	   << "- max n. jobs: " << s.n_jobs_max << endl
	   << "- svc time: " << s.svc_time << endl;
    }
    os << "* --- *\n";
  }

  friend ostream &operator<<(ostream &os, const Engine &e) {
    //os << fixed << setprecision(PRECISION);
    os << "#" << e.n_events << "\t"
       << "[" << e.time << "] (";
    unsigned int i=0;
    for(; i<e.queues.size()-1; ++i)
      os << *e.queues[i] << ": " << e.queues[i]->get_n_jobs() << ", ";
    os << *e.queues[i] << ": " << e.queues[i]->get_n_jobs()
       << ")\t| " << e.event_list;
    return os;
  }

private:
  //state variables
  unsigned long n_events; //n. scheduled events
  double time, old_time; //the clock

  //queues
  vector<Queue *> queues;

  //data-structures
  Event_List event_list;

#ifdef LOG
  ofstream &tracefile;
#endif
};
#endif
